package cn.com.easy.pay.alipay.www.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;

import cn.com.easy.exception.BusinessException;
import cn.com.easy.pay.alipay.www.config.AlipayConfig;
import cn.com.easy.pay.alipay.www.util.AlipayNotify;

/**
 * 阿里支付宝，异步回调，基本控制器(spring mvc)
 * 
 * @author nibili 2017年4月1日
 * 
 */
public abstract class AlipayNotifyBaseController {

	private Logger logger = LoggerFactory.getLogger(AlipayNotifyBaseController.class);

	/**
	 * 阿里配置参数
	 * 
	 * @return
	 * @author nibili 2017年4月1日
	 */
	public abstract AlipayConfig getAlipayConfig() throws BusinessException;

	/**
	 * 不要做异常处理，让控制器来做，控制器可以把结果返回给支付宝<br/>
	 * // 付款完成后，支付宝系统发送该交易状态通知<br/>
	 * 支付宝支付成功， 异步回调，业务回调方法<br/>
	 * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，<br/>
	 * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），<br/>
	 * 3、校验通知中的seller_id（或者seller_email
	 * )是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），<br/>
	 * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。<br/>
	 * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。<br/>
	 * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
	 * 
	 * @author nibili 2017年4月1日
	 */
	public abstract void notityTradeSuccessCallback(NotifyParam notifyParam) throws BusinessException;

	/**
	 * 不要做异常处理，让控制器来做，控制器可以把结果返回给支付宝<br/>
	 * // 退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知<br/>
	 * 支付宝支付完成， 异步回调，业务回调方法 * <br/>
	 * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，<br/>
	 * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），<br/>
	 * 3、校验通知中的seller_id（或者seller_email
	 * )是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），<br/>
	 * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。<br/>
	 * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。<br/>
	 * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
	 * 
	 * @author nibili 2017年4月1日
	 */
	public abstract void notityTradeFinishCallback(NotifyParam notifyParam) throws BusinessException;

	/**
	 * 功能：支付宝服务器异步通知页面 版本：3.3 日期：2012-08-17 说明：
	 * 以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
	 * 该代码仅供学习和研究支付宝接口使用，只是提供一个参考。
	 * 
	 * //***********页面功能说明*********** 创建该页面文件时，请留心该页面文件中无任何HTML代码及空格。
	 * 该页面不能在本机电脑测试，请到服务器上做测试。请确保外部可以访问该页面。
	 * 该页面调试工具请使用写文本函数logResult，该函数在com.alipay.util文件夹的AlipayCore.java类文件中
	 * 如果没有收到该页面返回的 success 信息，支付宝会在24小时内按一定的时间策略重发通知
	 * //********************************
	 * 
	 * @param request
	 * 
	 * @param response
	 * 
	 * @author nibili 2017年4月1日
	 */
	@RequestMapping("/notify")
	public void notify(HttpServletRequest request, HttpServletResponse response, NotifyParam notifyParam) {
		try {
			// 获取支付宝POST过来反馈信息
			Map<String, String> params = new HashMap<String, String>();
			Map<String, String[]> requestParams = request.getParameterMap();
			for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
				String name = (String) iter.next();
				String[] values = (String[]) requestParams.get(name);
				String valueStr = "";
				for (int i = 0; i < values.length; i++) {
					valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
				}
				// 乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
				// valueStr = new String(valueStr.getBytes("ISO-8859-1"),
				// "gbk");
				params.put(name, valueStr);
			}
			// 获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
			if (AlipayNotify.verify(params, getAlipayConfig())) {// 验证成功
				// ////////////////////////////////////////////////////////////////////////////////////////
				// 请在这里加上商户的业务逻辑程序代码
				// ——请根据您的业务逻辑来编写程序（以下代码仅作参考）——
				if (notifyParam.getTrade_status().equals("TRADE_FINISHED")) {
					// 判断该笔订单是否在商户网站中已经做过处理
					// 如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
					// 请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
					// 如果有做过处理，不执行商户的业务程序
					notityTradeFinishCallback(notifyParam);
					// 注意：
					// 退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
				} else if (notifyParam.getTrade_status().equals("TRADE_SUCCESS")) {
					// 判断该笔订单是否在商户网站中已经做过处理
					// 如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
					// 请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
					// 如果有做过处理，不执行商户的业务程序
					notityTradeSuccessCallback(notifyParam);
					// 注意：
					// 付款完成后，支付宝系统发送该交易状态通知
				}

				// ——请根据您的业务逻辑来编写程序（以上代码仅作参考）——

				response.getOutputStream().print("success"); // 请不要修改或删除
				// ////////////////////////////////////////////////////////////////////////////////////////
			} else {// 验证失败
				throw new BusinessException("支付宝验证失败");
			}
		} catch (BusinessException ex) {
			try {
				response.getOutputStream().print("fail");
			} catch (IOException e) {
				e.printStackTrace();
			}
			logger.info("支付宝nofity业务代码异常", ex);
		} catch (Exception ex) {
			try {
				response.getOutputStream().print("fail");
			} catch (IOException e) {
				e.printStackTrace();
			}
			logger.info("支付宝nofity异常", ex);
		}
	}

	/**
	 * 支付宝异步回调返回的参数<br/>
	 * 详参：https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.AWMdg8&
	 * treeId=62&articleId=104743&docType=1#s3
	 * 
	 * @author nibili 2017年4月1日
	 * 
	 */
	public static class NotifyParam {
		/* ==================基本参数=============== */
		/** 通知时间 Date 通知的发送时间。格式为yyyy-MM-dd HH:mm:ss。 */
		private String notify_time;
		/** 通知类型 String 通知的类型。 */
		private String notify_type;
		/** 通知校验ID String 通知校验ID。 */
		private String notify_id;
		/** 签名方式 String DSA、RSA、MD5三个值可选，必须大写。 */
		private String sign_type;
		/** 签名 String 请参见本文档“附录：签名与验签”。 */
		private String sign;

		/* ================业务参数============ */
		/** 商户网站唯一订单号 */
		private String out_trade_no;
		/** 商品名称 */
		private String subject;
		/** 支付类型 */
		private String payment_type;
		/** 支付宝交易号 */
		private String trade_no;
		/** 交易状态 */
		private String trade_status;
		/** 交易创建时间 */
		private String gmt_create;
		/** 交易付款时间 */
		private String gmt_payment;
		/** 交易关闭时间 */
		private String gmt_close;
		/** 退款状态 */
		private String refund_status;
		/** 退款时间 */
		private String gmt_refund;
		/** 卖家支付宝账号 */
		private String seller_email;
		/** 买家支付宝账号 */
		private String buyer_email;
		/** 卖家支付宝账户号 */
		private String seller_id;
		/** 买家支付宝账户号 */
		private String buyer_id;
		/** 商品单价 */
		private String price;
		/** 交易金额 */
		private String total_fee;
		/** 购买数量 */
		private String quantity;
		/** 商品描述 */
		private String body;
		/** 折扣 */
		private String discount;
		/** 是否调整总价 */
		private String is_total_fee_adjust;
		/** 是否使用红包买家 */
		private String use_coupon;
		/** 公用回传参数 */
		private String extra_common_param;
		/** 是否扫码支付 */
		private String business_scene;

		/**
		 * get ==================基本参数===============
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getNotify_time() {
			return notify_time;
		}

		/**
		 * set ==================基本参数===============
		 * 
		 * @param notify_time
		 * @author nibili 2017年4月1日
		 */
		public void setNotify_time(String notify_time) {
			this.notify_time = notify_time;
		}

		/**
		 * get 通知类型String通知的类型。
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getNotify_type() {
			return notify_type;
		}

		/**
		 * set 通知类型String通知的类型。
		 * 
		 * @param notify_type
		 * @author nibili 2017年4月1日
		 */
		public void setNotify_type(String notify_type) {
			this.notify_type = notify_type;
		}

		/**
		 * get 通知校验IDString通知校验ID。
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getNotify_id() {
			return notify_id;
		}

		/**
		 * set 通知校验IDString通知校验ID。
		 * 
		 * @param notify_id
		 * @author nibili 2017年4月1日
		 */
		public void setNotify_id(String notify_id) {
			this.notify_id = notify_id;
		}

		/**
		 * get 签名方式StringDSA、RSA、MD5三个值可选，必须大写。
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getSign_type() {
			return sign_type;
		}

		/**
		 * set 签名方式StringDSA、RSA、MD5三个值可选，必须大写。
		 * 
		 * @param sign_type
		 * @author nibili 2017年4月1日
		 */
		public void setSign_type(String sign_type) {
			this.sign_type = sign_type;
		}

		/**
		 * get 签名String请参见本文档“附录：签名与验签”。
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getSign() {
			return sign;
		}

		/**
		 * set 签名String请参见本文档“附录：签名与验签”。
		 * 
		 * @param sign
		 * @author nibili 2017年4月1日
		 */
		public void setSign(String sign) {
			this.sign = sign;
		}

		/**
		 * get =============
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getOut_trade_no() {
			return out_trade_no;
		}

		/**
		 * set =============
		 * 
		 * @param out_trade_no
		 * @author nibili 2017年4月1日
		 */
		public void setOut_trade_no(String out_trade_no) {
			this.out_trade_no = out_trade_no;
		}

		/**
		 * get 商品名称
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getSubject() {
			return subject;
		}

		/**
		 * set 商品名称
		 * 
		 * @param subject
		 * @author nibili 2017年4月1日
		 */
		public void setSubject(String subject) {
			this.subject = subject;
		}

		/**
		 * get 支付类型
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getPayment_type() {
			return payment_type;
		}

		/**
		 * set 支付类型
		 * 
		 * @param payment_type
		 * @author nibili 2017年4月1日
		 */
		public void setPayment_type(String payment_type) {
			this.payment_type = payment_type;
		}

		/**
		 * get 支付宝交易号
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getTrade_no() {
			return trade_no;
		}

		/**
		 * set 支付宝交易号
		 * 
		 * @param trade_no
		 * @author nibili 2017年4月1日
		 */
		public void setTrade_no(String trade_no) {
			this.trade_no = trade_no;
		}

		/**
		 * get 交易状态
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getTrade_status() {
			return trade_status;
		}

		/**
		 * set 交易状态
		 * 
		 * @param trade_status
		 * @author nibili 2017年4月1日
		 */
		public void setTrade_status(String trade_status) {
			this.trade_status = trade_status;
		}

		/**
		 * get 交易创建时间
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getGmt_create() {
			return gmt_create;
		}

		/**
		 * set 交易创建时间
		 * 
		 * @param gmt_create
		 * @author nibili 2017年4月1日
		 */
		public void setGmt_create(String gmt_create) {
			this.gmt_create = gmt_create;
		}

		/**
		 * get 交易付款时间
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getGmt_payment() {
			return gmt_payment;
		}

		/**
		 * set 交易付款时间
		 * 
		 * @param gmt_payment
		 * @author nibili 2017年4月1日
		 */
		public void setGmt_payment(String gmt_payment) {
			this.gmt_payment = gmt_payment;
		}

		/**
		 * get 交易关闭时间
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getGmt_close() {
			return gmt_close;
		}

		/**
		 * set 交易关闭时间
		 * 
		 * @param gmt_close
		 * @author nibili 2017年4月1日
		 */
		public void setGmt_close(String gmt_close) {
			this.gmt_close = gmt_close;
		}

		/**
		 * get 退款状态
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getRefund_status() {
			return refund_status;
		}

		/**
		 * set 退款状态
		 * 
		 * @param refund_status
		 * @author nibili 2017年4月1日
		 */
		public void setRefund_status(String refund_status) {
			this.refund_status = refund_status;
		}

		/**
		 * get 退款时间
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getGmt_refund() {
			return gmt_refund;
		}

		/**
		 * set 退款时间
		 * 
		 * @param gmt_refund
		 * @author nibili 2017年4月1日
		 */
		public void setGmt_refund(String gmt_refund) {
			this.gmt_refund = gmt_refund;
		}

		/**
		 * get 卖家支付宝账号
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getSeller_email() {
			return seller_email;
		}

		/**
		 * set 卖家支付宝账号
		 * 
		 * @param seller_email
		 * @author nibili 2017年4月1日
		 */
		public void setSeller_email(String seller_email) {
			this.seller_email = seller_email;
		}

		/**
		 * get 买家支付宝账号
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getBuyer_email() {
			return buyer_email;
		}

		/**
		 * set 买家支付宝账号
		 * 
		 * @param buyer_email
		 * @author nibili 2017年4月1日
		 */
		public void setBuyer_email(String buyer_email) {
			this.buyer_email = buyer_email;
		}

		/**
		 * get 卖家支付宝账户号
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getSeller_id() {
			return seller_id;
		}

		/**
		 * set 卖家支付宝账户号
		 * 
		 * @param seller_id
		 * @author nibili 2017年4月1日
		 */
		public void setSeller_id(String seller_id) {
			this.seller_id = seller_id;
		}

		/**
		 * get 买家支付宝账户号
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getBuyer_id() {
			return buyer_id;
		}

		/**
		 * set 买家支付宝账户号
		 * 
		 * @param buyer_id
		 * @author nibili 2017年4月1日
		 */
		public void setBuyer_id(String buyer_id) {
			this.buyer_id = buyer_id;
		}

		/**
		 * get 商品单价
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getPrice() {
			return price;
		}

		/**
		 * set 商品单价
		 * 
		 * @param price
		 * @author nibili 2017年4月1日
		 */
		public void setPrice(String price) {
			this.price = price;
		}

		/**
		 * get 交易金额
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getTotal_fee() {
			return total_fee;
		}

		/**
		 * set 交易金额
		 * 
		 * @param total_fee
		 * @author nibili 2017年4月1日
		 */
		public void setTotal_fee(String total_fee) {
			this.total_fee = total_fee;
		}

		/**
		 * get 购买数量
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getQuantity() {
			return quantity;
		}

		/**
		 * set 购买数量
		 * 
		 * @param quantity
		 * @author nibili 2017年4月1日
		 */
		public void setQuantity(String quantity) {
			this.quantity = quantity;
		}

		/**
		 * get 商品描述
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getBody() {
			return body;
		}

		/**
		 * set 商品描述
		 * 
		 * @param body
		 * @author nibili 2017年4月1日
		 */
		public void setBody(String body) {
			this.body = body;
		}

		/**
		 * get 折扣
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getDiscount() {
			return discount;
		}

		/**
		 * set 折扣
		 * 
		 * @param discount
		 * @author nibili 2017年4月1日
		 */
		public void setDiscount(String discount) {
			this.discount = discount;
		}

		/**
		 * get 是否调整总价
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getIs_total_fee_adjust() {
			return is_total_fee_adjust;
		}

		/**
		 * set 是否调整总价
		 * 
		 * @param is_total_fee_adjust
		 * @author nibili 2017年4月1日
		 */
		public void setIs_total_fee_adjust(String is_total_fee_adjust) {
			this.is_total_fee_adjust = is_total_fee_adjust;
		}

		/**
		 * get 是否使用红包买家
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getUse_coupon() {
			return use_coupon;
		}

		/**
		 * set 是否使用红包买家
		 * 
		 * @param use_coupon
		 * @author nibili 2017年4月1日
		 */
		public void setUse_coupon(String use_coupon) {
			this.use_coupon = use_coupon;
		}

		/**
		 * get 公用回传参数
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getExtra_common_param() {
			return extra_common_param;
		}

		/**
		 * set 公用回传参数
		 * 
		 * @param extra_common_param
		 * @author nibili 2017年4月1日
		 */
		public void setExtra_common_param(String extra_common_param) {
			this.extra_common_param = extra_common_param;
		}

		/**
		 * get 是否扫码支付
		 * 
		 * @return
		 * @author nibili 2017年4月1日
		 */
		public String getBusiness_scene() {
			return business_scene;
		}

		/**
		 * set 是否扫码支付
		 * 
		 * @param business_scene
		 * @author nibili 2017年4月1日
		 */
		public void setBusiness_scene(String business_scene) {
			this.business_scene = business_scene;
		}

	}
}
